Chapter 6 ADDITIONAL SCALAR TYPES OUR OWN TYPES _________________________________________________________________ Most of the example programs in this tutorial have used the predefined type INTEGER for illustrating various concepts, and it is an excellent choice due to its versatility. There are other types available because they are part of the Ada definition, and we can define our own types for special purposes. This chapter will illustrate to you some of the reasons for doing so. A complete description of types will be given in the next chapter but first we will learn how to use some of them. We are caught in a dilemma like the proverbial, "which came first, the chicken or the egg?", and must select which to define first. We have chosen to illustrate usage in this chapter, and then give the details of type definition in the next chapter. A FEW integer CLASS TYPES _________________________________________________________________ Examine the file named ALLINT.ADA for some ================ examples of how and why we can use our own type ALLINT.ADA definitions. The three types illustrated in ================ lines 10 through 12 are available with all Ada compilers because they are so versatile and useful, and because they are required by the Ada programming language. As we have said before, the type INTEGER defines a variable that can have any whole value between -32768 to 32767 on most microcomputers and some minicomputers too. It should be pointed out that most minicomputers use a much larger range as standard. The type NATURAL defines a variable from 0 to 32767, and the type POSITIVE covers the range from 1 to 32767 on most microcomputers. Consider the word "most" in the last paragraph, and think about the problems you could have if you wrote a program that depended on a particular variable covering the listed range, and tried to move the program to a different machine which used a different range. You could be faced with a large rewrite problem in order to get the program to work on the new computer. HOW CAN WE HELP SOLVE PORTABILITY PROBLEMS _________________________________________________________________ Suppose we defined our type to cover a certain range, such as illustrated in line 14 of the example program, and moved the Page 6-1 Chapter 6 - Additional Scalar Types program to another computer. According to the definition of Ada, the new compiler would be obligated to create a type for us that would cover the given range, or give us a compile time error telling us that the hardware simply could not support the defined range. In this case, due to the rather small range requested, any meaningful compiler and machine combination would be able to cover the defined range, and we would have a program that would run in spite of differences in the way the standard types were defined. Good programming practice, especially if the source code may need to be moved to other computers, would define all ranges explicitly and avoid the implementation defined limits built into the compiler. Two new types are defined in lines 14 and 15, and the program uses some new attributes to illustrate the new types. In lines 24 and 27, we use two attributes which we have used before, but in lines 31 and 34, we use two new attributes. In order for the system to create a type which covers a range of -1000 to 24000, it must use a structure with enough binary bits to cover the given range. The range is not composed of binary limits so the system will have to define enough bits to cover this range and a little more. It will probably define some number of 8-bit bytes and the range covered by the full pattern, as defined, is called the base range. The two new attributes give the limits of the base selected by the system. The base limits will probably be -32768 to 32767 if you are using a microcomputer, as you will see when you execute the program. DO YOU HAVE A SMART COMPILER? _________________________________________________________________ The type illustrated here named MY_SHORT has defined limits of -12 and 127, a relatively small range. It is small enough that it can fit into a base range of -128 to 127, which could be stored in a single 8-bit byte. If your compiler is smart enough to realize that, it could use a single 8-bit byte to store every variable of this type, and if you had a lot of these to store, it would save you a lot of memory. You will probably find however, that most compilers will simply use the full INTEGER range for the base type of even this small number. Four attributes of two different types are displayed on the monitor for your information. You can see from the results of running this program exactly how your compiler stores these two types. THE in AND not in OPERATORS _________________________________________________________________ We have a new operator to learn about now, the in operator illustrated in line 48. If the variable Index, which has a current value of 345 due to initialization, is within the defined range of Page 6-2 Chapter 6 - Additional Scalar Types the subtype MY_SUBTYPE, a BOOLEAN type TRUE will be returned, otherwise a BOOLEAN type FALSE is returned. This result can be assigned to a BOOLEAN variable or used for a boolean decision as shown. In this case, the value of Index is not in the range of MY_SUBTYPE so a FALSE is returned and the message will not be output. Another operation is illustrated in line 53 which is the not in operation, and should be self explanatory. You should be able to see that the message in line 53 will be displayed. The in and not in operators are further illustrated in lines 56 and 60 where an explicit range is used for the test range. Be sure to compile and run this program and observe the output. Here is a chance for you to see if you have a smart compiler. THE ENUMERATED TYPE _________________________________________________________________ Examine the program named ENUM.ADA for our first ================ look at an enumerated type and how it is used in ENUM.ADA a program. Line 7 is the first definition of an ================ enumerated type, and uses the reserved words type and is as shown. The type name is given between the two reserved words and the values which a variable of this type is allowed to have assigned to it are given as a list within parentheses. The values actually represent numerical values from 0 up to that value required for the largest value, in this case 6, since the numbering will be assigned from 0 to 6. In line 20, the variable named Day_Of_Week is declared to be of type DAY, so it can be assigned any of the 7 values listed for the type DAY, and no others. We could assign the values 0, 1, 2,.. 6 to represent the 7 days of the week and use the numerical values within the program, but by using the enumerated type, we can refer to Sunday as SUN, Monday as MON, etc., making the program much clearer and easy to follow. Enumerated types are always locally defined because there are no predefined enumerated types in the Ada language. There is one exception to this, because the BOOLEAN type is an enumerated type with two possible values, but it has some special properties available with no other enumerated variables. These will be discussed using the next example program. Jumping ahead to the executable code in the current example program, we illustrate assignment in line 28, where we assign the value of WED to the variable Day_Of_Week. Lines 29 and 30 illustrate the FIRST and LAST attributes which we have seen before for integer type variables. Just as -32768 is the lowest possible value that can be assigned to an INTEGER type variable, MON is the lowest, and hence the first, value that can be assigned to a variable of type DAY. Page 6-3 Chapter 6 - Additional Scalar Types TWO NEW ATTRIBUTES _________________________________________________________________ Lines 31 and 32 illustrate the attributes PRED, which means the predecessor, and SUCC, which means the successor. PRED returns the value of the predecessor of the present value of the variable used as an argument. Since the variable Day_Of_Week was assigned the value of SUN in line 30, and the day just prior to SUN is SAT, SAT is assigned to the variable Day_Of_Week in line 31. It is an error to attempt to take the PRED of a variable which contains the first value in the available list, and will result in raising the exception Range_Error. Likewise, an attempt to take the SUCC of any variable that is at its maximum value will result in the exception Range_Error being raised. Exceptions will be covered in detail later in this tutorial. At this time simply remember that an exception refers to an exceptional condition or an error. WHAT IS A SUBTYPE OF AN ENUMERATED TYPE? _________________________________________________________________ In lines 8 and 9, we define two subtypes of the type DAY which will have all the characteristics of type DAY except for a more restricted range. A variable that is declared to be of type PLAY_DAY can be assigned either of two values, SAT or SUN. SAT will have a numerical value of 5, and SUN will have a numerical value of 6, both of these being inherited from the parent type, DAY. Thus in line 32, we use the attribute FIRST to get the first day of type PLAY_DAY, which will be SAT, then use the attribute SUCC to get the successor of that value, which will be SUN. Notice how the attributes can be combined to obtain the needed information. A subtype is assignment compatible with its parent type. We will discuss subtypes in greater detail in the next chapter of this tutorial. NOW FOR THE POS AND VAL ATTRIBUTES _________________________________________________________________ The POS attribute will return a value of type universal_integer, the value representing the position of the enumerated value within the parentheses as shown in lines 33 and 34. The VAL attribute will return the enumerated value of the numerical value included in the parentheses. Notice that if the type DAY in line 35 were changed to PLAY_DAY, an error would be returned, since that is an illegal enumerated value for that type. The error would be returned by raising the exception Range_Error. Page 6-4 Chapter 6 - Additional Scalar Types WHAT ABOUT ENUMERATED ASSIGNMENTS? _________________________________________________________________ The value of Happy_Day can be assigned to Day_Of_Week at any time because they are both of the same type, and any value that can be legally assigned to Happy_Day can also be assigned to Day_Of_Week. Day_Of_Week cannot always be assigned to Happy_Day however, because Day_Of_Week is permitted to contain some values which are not legal to assign to Happy_Day. This would illustrate that some care must be exercised when using the enumerated type, but if used properly, it can help in program debugging by the use of the strong type-checking defined into the Ada language. USING ENUMERATED TYPES FOR CONTROL _________________________________________________________________ The loop in lines 37 through 40 covers exactly the range covered by the subtype WORK_DAY, so we can use it in the range part of the definition of the loop. When you run this program, you will see that the loop will be executed exactly five times. Lines 42 through 51 contain two relational checks on the variable Today to illustrate that the enumerated type variable can be used in a BOOLEAN expression. All of the boolean operators are available, which includes the following list, and no others; = equality /= inequality > greater than >= greater than or equal to < less than <= less than or equal to No mathematical operations are available with enumerated type variables. Assignments are available as illustrated in the present example program. WHAT IS QUALIFICATION? _________________________________________________________________ In lines 53 and 54, we assign the same value to two different enumerated type variables. At least it seems to be the same value. In actuality, they are two different values with the same name, namely SUN. Because Ada does such strong type checking, it is smart enough to realize that they are actually two different constants and it will select the one that it needs for each statement based on the type of the variable to which it will be assigned. Page 6-5 Chapter 6 - Additional Scalar Types Lines 56 and 57 make the identical assignments by qualifying which value you are interested in, but in this case, the qualifications are unnecessary. There could be a case when you would need to tell the system which value of SUN you are interested in. Qualification uses the type followed by a "tick", or apostrophe, prior to the enumeration value. OUTPUTTING ENUMERATED VARIABLES _________________________________________________________________ The statements in lines 59 and 60 output the current value of the variable Today, and the predecessor of the current value. Finally, the same values are output for the variable Big_Sphere, and when you run the program, you will see that the same value is output for the first value in each line, but the second values differ for the two variables. Note the four extra lines given in program lines 14 through 18. These are used to tell the system how to output enumerated variables, and we will cover the essentials of how this works very soon. The in and not in operators which we studied in the last program are available for use with the enumerated type variable. In fact, they are available with all discrete types. Be sure to compile and run this program and after studying the results, see if you can modify the program to output additional enumerated values. THE BOOLEAN VARIABLE _________________________________________________________________ The program named BOOLVARS.ADA is an ================ illustration of how to use the BOOLEAN variable, BOOLVARS.ADA which is actually a special case of an ================ enumerated variable. Every enumerated type must be defined by the user, before use, with the exception of the predefined BOOLEAN type. It is simply an enumerated type with two possible values, TRUE or FALSE. Since it is an enumerated type, all of the operations available with the enumerated type are available with the BOOLEAN type, including all six of the relational operators, the assignment operator, the attributes, and no mathematical operators. THE LOGICAL OPERATORS _________________________________________________________________ The BOOLEAN type has some of its own unique operators that are available with no other types, the logical operators. The logical operators were defined earlier, but are repeated here as a complete list. Page 6-6 Chapter 6 - Additional Scalar Types and logical and operation or logical or operation xor exclusive or of two values not inversion of the value and then short circuit and operation or else short circuit or operation It should be pointed out that FALSE is of less numerical value than TRUE, by definition, and in actuality, the value of FALSE is 0, and the value of TRUE is 1. The program illustrates how to output BOOLEAN values in lines 29 and 30, after the package instantiation in lines 7 and 8. Notice that the Enumeration_IO library is used for BOOLEAN output illustrating again that BOOLEAN is a special case of the enumerated type. Be sure to compile and execute this program to see that it really does compile as stated. Someday, you will need the ability to display the BOOLEAN results as is done in this program. SOME USELESS ATTRIBUTES OF INTEGER TYPES _________________________________________________________________ It may seem silly to illustrate some useless =============== attributes but the program named INCRINT.ADA INCRINT.ADA does that very thing. The POS attribute of an =============== integer variable is defined as being the number itself, and the VAL is also the number. The SUCC of an integer variable is the next number, and the PRED is the predecessor. These last two attributes could be useful for incrementing or decrementing a variable in a program, but good programming practice would forbid such a use of these attributes. You should use the very clear and easy to understand method of adding one to the value and assigning the result back to the variable, as illustrated in line 17 of the program. Even though these are really useless at this point in time, the fact that this can be done will be very useful when we get to the study of generics later in this tutorial. Compile and run this program, adding some output to gain some of your own programming experience. FLOATING POINT VARIABLES _________________________________________________________________ Examine the program named FLOATVAR.ADA for ================ examples of nearly all operations possible with FLOATVAR.ADA floating point numbers. ================ Page 6-7 Chapter 6 - Additional Scalar Types We begin, in lines 7 and 8, by defining two constants, the second being defined in terms of the first. Remember that any thing used in an Ada program must be previously defined, and you will know all of the rules for defining a value in terms of some other value. The two constants are of type universal_real, so they can be used with any of the various real types we will encounter in this program. We declare a variable named R in line 10 of type FLOAT, which is defined by the compiler writer, then two new types of floating point numbers, and we finally declare six variables of various types. Two additional floating point types, SHORT_FLOAT and LONG_FLOAT are defined as optional by the LRM and may be available with your compiler. You can find out by checking the documentation supplied with your compiler or by declaring variables of those types to see if the compiler will accept the declarations. If they do exist, you can determine their limits by using attributes as defined below. HOW TO DECLARE A NEW FLOATING POINT TYPE _________________________________________________________________ The reserved word digits used in line 12 tells the compiler that you don't care how many bytes of storage it uses to define the number, but it must store at least 7 significant digits for every variable of type MY_FLOAT. Line 13 requests a minimum of 15 significant digits for every variable of type MY_LONG_FLOAT. Line 10, on the other hand, only requires that it be a floating point number, and the compiler writer has the option of using as many significant digits as he desires to implement variables of this type. If you wrote a program that ran well with one compiler, it may not run properly with a different compiler, either because the new one did not use enough significant digits, or because the new one used far more causing your program to run out of storage space or run too slowly. The forms in lines 12 and 13 are therefore preferred for portability purposes. More will be said about declaring floating point types in the next chapter of this tutorial. FLOATING POINT LITERALS _________________________________________________________________ The distinguishing characteristic that defines a floating point number is the use of a decimal point. Ada requires at least one digit before and after the decimal point, although either or both may be zeros. Single embedded underlines are allowed to improve readability, but cannot be adjacent to the decimal point. The underlines are ignored by the compiler, and have no significance. Any radix from 2 through 16 may be used by first giving the radix, then enclosing the number in pound (#) characters. The base 10 is the default and need not be specified. Exponential notation can be used, the exponent being to the same base as that indicated by Page 6-8 Chapter 6 - Additional Scalar Types the radix. A binary floating point literal is illustrated in line 31 of the program and you can see that the radix is similar to that used for integer class literals. FLOATING POINT MATHEMATICAL OPERATORS _________________________________________________________________ Lines 29 through 35 illustrate the mathematical operators available with floating point variables and should be self explanatory with the exception of the exponential operator. This can use only an integer type of exponent but it can be either positive or negative. Of course, zero is also permissible. All six logical comparisons are available with floating point variables as illustrated in lines 38 through 43. The next two lines, 45 and 46, illustrate some multiple mathematical operations. As with all variables, the types must agree within all mathematical and logical operations and the result must be assigned to the right type of variable, or a type error will be generated at compile time. In line 46, the variable Area must be transformed in type prior to being assigned to Cover since they are of different types. The entire statement will be evaluated as of type MY_LONG_FLOAT, since that will be the final result. The constant 27.3, and the constant PI, will be transformed automatically from universal_real to MY_LONG_FLOAT prior to the multiplications. NOW TO OUTPUT SOME FLOATING POINT VALUES _________________________________________________________________ In lines 24 and 25 we instantiate a copy of the Float_IO package for use with the type MY_FLOAT and use it in lines 49 through 55. The variable Area will be output in a default exponential notation in line 49, but with 5 digits prior to the decimal point in line 50. Line 54 adds an additional 5, which will cause 5 digits to be output following the decimal point, and the fourth field, in this case a zero, causes the output to be written with a zero exponent, or no exponential notation. FLOATING POINT ATTRIBUTES _________________________________________________________________ Floating point variables and types are no different from the scalar types concerning attributes available for your use, except that there are different attributes available. Lines 58 through 72 illustrate the use of some of the available attributes. The attribute named DIGITS, gives the number of significant digits available with the specific type, and the return is a universal_integer type. Page 6-9 Chapter 6 - Additional Scalar Types The attributes named SMALL and LARGE give the smallest and largest numbers available with the corresponding type, and the attributes named FIRST and LAST combined with the BASE attribute as shown in lines 68 and 71, define the extreme values as used by the underlying base type of the actual user's type. All four of these attributes return a value of the type universal_real, and are displayed on the monitor for your information. Note that there are other attributes available with the floating point type, but only these will be elaborated upon at this time. See appendix A of the LRM for additional information on attributes. This appendix lists all of the attributes available with an Ada system. Compile and run this program and observe the output. The actual output is the clearest description of the Put procedure when used with floating point numbers. Study the result of the attribute outputs before continuing on to the next example program. FIXED POINT VARIABLES _________________________________________________________________ Fixed point variables are a relatively new =============== concept and may be a bit confusing, but the file FIXED.ADA named FIXED.ADA will illustrate the use of a few =============== fixed point variables. Line 9 defines a fixed point type as having a range of -40.0 to 120.0, and a delta of 0.1 which means that a variable of this number can only have a value that is accurate to one digit after the decimal point. There are therefore a fixed number of digits before and after the decimal point, hence the name of this type of variable. A fixed point number will always be exact since it is defined that way. There can never be a gradual accumulation of error with a fixed point variable. In order to completely understand the fixed point type, one would require a complete understanding of numerical analysis, which is beyond the scope of this tutorial. The program before you will illustrate how to use this type, but no attempt will be made to explain why it should be used. There are no predefined fixed point types, so it is up to the programmer to define every fixed point type needed, as illustrated in lines 9 and 10. The reserved word delta denotes a fixed point type and a range is required for every fixed point type. Lines 12 and 13 are used to declare a few variables for use in the program, then lines 17 through 20 instantiate the package Fixed_IO for use with our two fixed point types. Page 6-10 Chapter 6 - Additional Scalar Types HOW DO WE USE FIXED POINT TYPES? _________________________________________________________________ Output of a fixed point type uses the same format as that defined for floating point data as shown in line 27. When we come to arithmetical operations, we find some funny rules which we will simply state, and make no attempt to justify. Variables of the same fixed point types can be added and subtracted freely, provided the results are within the defined range, just like floating point type variables. Multiplication by a constant of type universal_integer is permitted, resulting in the same fixed point type we started with. Multiplication of two fixed point variables results in an anonymous type which must be explicitly converted to some predefined type, as illustrated in lines 33 and 34. The only operator available with the fixed types is the abs operator. Many attributes are available with the fixed point type, some of which are illustrated in lines 46 through 61. The attributes named DELTA, SMALL, and LARGE, each return a value which is of type universal_real, and must be converted to the users fixed type, by a type conversion, before the result can be used in the program. Line 48 illustrates the conversion within the Put procedure call. Lines 51 and 54 illustrates the explicit conversion to a FLOAT type, but since the results are to be used as FLOAT variables, and the universal_real type can be used directly as a FLOAT type, the conversion is not actually required. Be sure to compile and run this program and observe the output to see if it conforms to what you think it should do based on the previous discussion. Note that your compiler may not generate identical output as that listed in the result of execution due to different compiler defaults. MIXING VARIOUS TYPES _________________________________________________________________ Examine the program named MIXTYPES.ADA for ================ examples of using various types together. It is MIXTYPES.ADA meant to be an illustration of how to combine ================ some of the various types available in Ada. Many type transformations are illustrated in this program and should be easy for you to understand. Note especially, that the final result of lines 27, 28, and 29 will not necessarily be the same due to the rounding that takes place at different points in the calculations. Note that in Ada, conversion from real to integer always rounds rather than truncates. A value midway between the two integer values can go either way since it is not defined by the LRM but is left up to the implementor. Page 6-11 Chapter 6 - Additional Scalar Types Compile and execute this program to assure yourself that it will compile correctly. PROGRAMMING EXERCISES _________________________________________________________________ 1. Write a program to determine if LONG_INTEGER and SHORT_INTEGER types are available with your compiler. If they are available, use attributes to determine their characteristics. 2. Do the same thing as exercise 1 for the LONG_FLOAT and SHORT_FLOAT types. 3. Try to take the PRED of the first element of an enumerated variable to see what kind of a run-time error you get. Your compiler may be smart enough to warn you if you try to take it directly (i.e. - by using the first value in the parentheses), so you may need to assign a variable to the first value and take the PRED of the variable. Page 6-12